/*
 * Copyright (c) 1999, 2006, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.tools.javac.parser;

import static com.sun.tools.javac.parser.Token.AMP;
import static com.sun.tools.javac.parser.Token.ASSERT;
import static com.sun.tools.javac.parser.Token.CASE;
import static com.sun.tools.javac.parser.Token.CATCH;
import static com.sun.tools.javac.parser.Token.CLASS;
import static com.sun.tools.javac.parser.Token.COLON;
import static com.sun.tools.javac.parser.Token.COMMA;
import static com.sun.tools.javac.parser.Token.DEFAULT;
import static com.sun.tools.javac.parser.Token.DOT;
import static com.sun.tools.javac.parser.Token.ELLIPSIS;
import static com.sun.tools.javac.parser.Token.ELSE;
import static com.sun.tools.javac.parser.Token.ENUM;
import static com.sun.tools.javac.parser.Token.EOF;
import static com.sun.tools.javac.parser.Token.EQ;
import static com.sun.tools.javac.parser.Token.ERROR;
import static com.sun.tools.javac.parser.Token.EXTENDS;
import static com.sun.tools.javac.parser.Token.FINAL;
import static com.sun.tools.javac.parser.Token.FINALLY;
import static com.sun.tools.javac.parser.Token.GT;
import static com.sun.tools.javac.parser.Token.GTEQ;
import static com.sun.tools.javac.parser.Token.GTGT;
import static com.sun.tools.javac.parser.Token.GTGTEQ;
import static com.sun.tools.javac.parser.Token.GTGTGTEQ;
import static com.sun.tools.javac.parser.Token.IDENTIFIER;
import static com.sun.tools.javac.parser.Token.IMPLEMENTS;
import static com.sun.tools.javac.parser.Token.IMPORT;
import static com.sun.tools.javac.parser.Token.INSTANCEOF;
import static com.sun.tools.javac.parser.Token.INTERFACE;
import static com.sun.tools.javac.parser.Token.INTLITERAL;
import static com.sun.tools.javac.parser.Token.LBRACE;
import static com.sun.tools.javac.parser.Token.LBRACKET;
import static com.sun.tools.javac.parser.Token.LONGLITERAL;
import static com.sun.tools.javac.parser.Token.LPAREN;
import static com.sun.tools.javac.parser.Token.LT;
import static com.sun.tools.javac.parser.Token.MONKEYS_AT;
import static com.sun.tools.javac.parser.Token.NEW;
import static com.sun.tools.javac.parser.Token.PACKAGE;
import static com.sun.tools.javac.parser.Token.PLUSEQ;
import static com.sun.tools.javac.parser.Token.PLUSPLUS;
import static com.sun.tools.javac.parser.Token.QUES;
import static com.sun.tools.javac.parser.Token.RBRACE;
import static com.sun.tools.javac.parser.Token.RBRACKET;
import static com.sun.tools.javac.parser.Token.RPAREN;
import static com.sun.tools.javac.parser.Token.SEMI;
import static com.sun.tools.javac.parser.Token.STAR;
import static com.sun.tools.javac.parser.Token.STATIC;
import static com.sun.tools.javac.parser.Token.SUB;
import static com.sun.tools.javac.parser.Token.SUBSUB;
import static com.sun.tools.javac.parser.Token.SUPER;
import static com.sun.tools.javac.parser.Token.THROWS;
import static com.sun.tools.javac.parser.Token.TRUE;
import static com.sun.tools.javac.parser.Token.VOID;
import static com.sun.tools.javac.parser.Token.WHILE;
import static com.sun.tools.javac.util.ListBuffer.lb;

import java.util.HashMap;
import java.util.Map;

import android.annotation.SuppressLint;

import com.sun.tools.javac.code.BoundKind;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Source;
import com.sun.tools.javac.code.TypeTags;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree;
import com.sun.tools.javac.tree.JCTree.JCAssert;
import com.sun.tools.javac.tree.JCTree.JCBinary;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCBreak;
import com.sun.tools.javac.tree.JCTree.JCCase;
import com.sun.tools.javac.tree.JCTree.JCCatch;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCContinue;
import com.sun.tools.javac.tree.JCTree.JCDoWhileLoop;
import com.sun.tools.javac.tree.JCTree.JCErroneous;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCLiteral;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
import com.sun.tools.javac.tree.JCTree.JCNewClass;
import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
import com.sun.tools.javac.tree.JCTree.JCReturn;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCSwitch;
import com.sun.tools.javac.tree.JCTree.JCThrow;
import com.sun.tools.javac.tree.JCTree.JCTypeApply;
import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.JCTree.TypeBoundKind;
import com.sun.tools.javac.tree.TreeInfo;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Convert;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Log;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Options;
import com.sun.tools.javac.util.Position;

/**
 * The parser maps a token sequence into an abstract syntax tree. It operates by
 * recursive descent, with code derived systematically from an LL(1) grammar.
 * For efficiency reasons, an operator precedence scheme is used for parsing
 * binary operation expressions.
 * 
 * <p>
 * <b>This is NOT part of any supported API. If you write code that depends on
 * this, you do so at your own risk. This code and its internal interfaces are
 * subject to change or deletion without notice.</b>
 */
@SuppressLint({ "UseValueOf", "Assert" })
public class Parser {

	/** A factory for creating parsers. */
	public static class Factory {
		/** The context key for the parser factory. */
		protected static final Context.Key<Parser.Factory> parserFactoryKey = new Context.Key<Parser.Factory>();

		/** Get the Factory instance for this context. */
		public static Factory instance(Context context) {
			Factory instance = context.get(parserFactoryKey);
			if (instance == null)
				instance = new Factory(context);
			return instance;
		}

		final TreeMaker F;
		final Log log;
		final Keywords keywords;
		final Source source;
		final Name.Table names;
		final Options options;

		/** Create a new parser factory. */
		protected Factory(Context context) {
			context.put(parserFactoryKey, this);
			this.F = TreeMaker.instance(context);
			this.log = Log.instance(context);
			this.names = Name.Table.instance(context);
			this.keywords = Keywords.instance(context);
			this.source = Source.instance(context);
			this.options = Options.instance(context);
		}

		/**
		 * Create a new Parser.
		 * 
		 * @param S
		 *            Lexer for getting tokens while parsing
		 * @param keepDocComments
		 *            true if javadoc comments should be kept
		 * @param genEndPos
		 *            true if end positions should be generated
		 */
		public Parser newParser(Lexer S, boolean keepDocComments,
				boolean genEndPos) {
			if (!genEndPos)
				return new Parser(this, S, keepDocComments);
			else
				return new EndPosParser(this, S, keepDocComments);
		}
	}

	/**
	 * The number of precedence levels of infix operators.
	 */
	private static final int infixPrecedenceLevels = 10;

	/**
	 * The scanner used for lexical analysis.
	 */
	private Lexer S;

	/**
	 * The factory to be used for abstract syntax tree construction.
	 */
	protected TreeMaker F;

	/**
	 * The log to be used for error diagnostics.
	 */
	private Log log;

	/** The keyword table. */
	private Keywords keywords;

	/** The Source language setting. */
	private Source source;

	/** The name table. */
	private Name.Table names;

	/**
	 * Construct a parser from a given scanner, tree factory and log.
	 */
	protected Parser(Factory fac, Lexer S, boolean keepDocComments) {
		this.S = S;
		S.nextToken(); // prime the pump
		this.F = fac.F;
		this.log = fac.log;
		this.names = fac.names;
		this.keywords = fac.keywords;
		this.source = fac.source;
		@SuppressWarnings("unused")
		Options options = fac.options;
		this.allowGenerics = source.allowGenerics();
		this.allowVarargs = source.allowVarargs();
		this.allowAsserts = source.allowAsserts();
		this.allowEnums = source.allowEnums();
		this.allowForeach = source.allowForeach();
		this.allowStaticImport = source.allowStaticImport();
		this.allowAnnotations = source.allowAnnotations();
		this.keepDocComments = keepDocComments;
		if (keepDocComments)
			docComments = new HashMap<JCTree, String>();
		this.errorTree = F.Erroneous();
	}

	/**
	 * Switch: Should generics be recognized?
	 */
	boolean allowGenerics;

	/**
	 * Switch: Should varargs be recognized?
	 */
	boolean allowVarargs;

	/**
	 * Switch: should we recognize assert statements, or just give a warning?
	 */
	boolean allowAsserts;

	/**
	 * Switch: should we recognize enums, or just give a warning?
	 */
	boolean allowEnums;

	/**
	 * Switch: should we recognize foreach?
	 */
	boolean allowForeach;

	/**
	 * Switch: should we recognize foreach?
	 */
	boolean allowStaticImport;

	/**
	 * Switch: should we recognize annotations?
	 */
	boolean allowAnnotations;

	/**
	 * Switch: should we keep docComments?
	 */
	boolean keepDocComments;

	/**
	 * When terms are parsed, the mode determines which is expected: mode = EXPR
	 * : an expression mode = TYPE : a type mode = NOPARAMS : no parameters
	 * allowed for type mode = TYPEARG : type argument
	 */
	static final int EXPR = 1;
	static final int TYPE = 2;
	static final int NOPARAMS = 4;
	static final int TYPEARG = 8;

	/**
	 * The current mode.
	 */
	private int mode = 0;

	/**
	 * The mode of the term that was parsed last.
	 */
	private int lastmode = 0;

	/* ---------- error recovery -------------- */

	private JCErroneous errorTree;

	/**
	 * Skip forward until a suitable stop token is found.
	 */
	@SuppressWarnings("incomplete-switch")
	private void skip(boolean stopAtImport, boolean stopAtMemberDecl,
			boolean stopAtIdentifier, boolean stopAtStatement) {
		while (true) {
			switch (S.token()) {
			case SEMI:
				S.nextToken();
				return;
			case PUBLIC:
			case FINAL:
			case ABSTRACT:
			case MONKEYS_AT:
			case EOF:
			case CLASS:
			case INTERFACE:
			case ENUM:
				return;
			case IMPORT:
				if (stopAtImport)
					return;
				break;
			case LBRACE:
			case RBRACE:
			case PRIVATE:
			case PROTECTED:
			case STATIC:
			case TRANSIENT:
			case NATIVE:
			case VOLATILE:
			case SYNCHRONIZED:
			case STRICTFP:
			case LT:
			case BYTE:
			case SHORT:
			case CHAR:
			case INT:
			case LONG:
			case FLOAT:
			case DOUBLE:
			case BOOLEAN:
			case VOID:
				if (stopAtMemberDecl)
					return;
				break;
			case IDENTIFIER:
				if (stopAtIdentifier)
					return;
				break;
			case CASE:
			case DEFAULT:
			case IF:
			case FOR:
			case WHILE:
			case DO:
			case TRY:
			case SWITCH:
			case RETURN:
			case THROW:
			case BREAK:
			case CONTINUE:
			case ELSE:
			case FINALLY:
			case CATCH:
				if (stopAtStatement)
					return;
				break;
			}
			S.nextToken();
		}
	}

	private JCErroneous syntaxError(int pos, String key, Object... arg) {
		return syntaxError(pos, null, key, arg);
	}

	private JCErroneous syntaxError(int pos, List<JCTree> errs, String key,
			Object... arg) {
		setErrorEndPos(pos);
		reportSyntaxError(pos, key, arg);
		return toP(F.at(pos).Erroneous(errs));
	}

	private int errorPos = Position.NOPOS;

	/**
	 * Report a syntax error at given position using the given argument unless
	 * one was already reported at the same position.
	 */
	private void reportSyntaxError(int pos, String key, Object... arg) {
		if (pos > S.errPos() || pos == Position.NOPOS) {
			if (S.token() == EOF)
				log.error(pos, "premature.eof");
			else
				log.error(pos, key, arg);
		}
		S.errPos(pos);
		if (S.pos() == errorPos)
			S.nextToken(); // guarantee progress
		errorPos = S.pos();
	}

	/**
	 * Generate a syntax error at current position unless one was already
	 * reported at the same position.
	 */
	private JCErroneous syntaxError(String key) {
		return syntaxError(S.pos(), key);
	}

	/**
	 * Generate a syntax error at current position unless one was already
	 * reported at the same position.
	 */
	private JCErroneous syntaxError(String key, String arg) {
		return syntaxError(S.pos(), key, arg);
	}

	/**
	 * If next input token matches given token, skip it, otherwise report an
	 * error.
	 */
	public void accept(Token token) {
		if (S.token() == token) {
			S.nextToken();
		} else {
			setErrorEndPos(S.pos());
			reportSyntaxError(S.prevEndPos(), "expected",
					keywords.token2string(token));
		}
	}

	/**
	 * Report an illegal start of expression/type error at given position.
	 */
	JCExpression illegal(int pos) {
		setErrorEndPos(S.pos());
		if ((mode & EXPR) != 0)
			return syntaxError(pos, "illegal.start.of.expr");
		else
			return syntaxError(pos, "illegal.start.of.type");

	}

	/**
	 * Report an illegal start of expression/type error at current position.
	 */
	JCExpression illegal() {
		return illegal(S.pos());
	}

	/** Diagnose a modifier flag from the set, if any. */
	void checkNoMods(long mods) {
		if (mods != 0) {
			long lowestMod = mods & -mods;
			log.error(S.pos(), "mod.not.allowed.here", Flags
					.toString(lowestMod).trim());
		}
	}

	/* ---------- doc comments --------- */

	/**
	 * A hashtable to store all documentation comments indexed by the tree nodes
	 * they refer to. defined only if option flag keepDocComment is set.
	 */
	Map<JCTree, String> docComments;

	/**
	 * Make an entry into docComments hashtable, provided flag keepDocComments
	 * is set and given doc comment is non-null.
	 * 
	 * @param tree
	 *            The tree to be used as index in the hashtable
	 * @param dc
	 *            The doc comment to associate with the tree, or null.
	 */
	void attach(JCTree tree, String dc) {
		if (keepDocComments && dc != null) {
			// System.out.println("doc comment = ");System.out.println(dc);//DEBUG
			docComments.put(tree, dc);
		}
	}

	/* -------- source positions ------- */

	private int errorEndPos = -1;

	private void setErrorEndPos(int errPos) {
		if (errPos > errorEndPos)
			errorEndPos = errPos;
	}

	protected int getErrorEndPos() {
		return errorEndPos;
	}

	/**
	 * Store ending position for a tree.
	 * 
	 * @param tree
	 *            The tree.
	 * @param endpos
	 *            The ending position to associate with the tree.
	 */
	protected void storeEnd(JCTree tree, int endpos) {
	}

	/**
	 * Store ending position for a tree. The ending position should be the
	 * ending position of the current token.
	 * 
	 * @param t
	 *            The tree.
	 */
	protected <T extends JCTree> T to(T t) {
		return t;
	}

	/**
	 * Store ending position for a tree. The ending position should be greater
	 * of the ending position of the previous token and errorEndPos.
	 * 
	 * @param t
	 *            The tree.
	 */
	protected <T extends JCTree> T toP(T t) {
		return t;
	}

	/**
	 * Get the start position for a tree node. The start position is defined to
	 * be the position of the first character of the first token of the node's
	 * source text.
	 * 
	 * @param tree
	 *            The tree node
	 */
	public int getStartPos(JCTree tree) {
		return TreeInfo.getStartPos(tree);
	}

	/**
	 * Get the end position for a tree node. The end position is defined to be
	 * the position of the last character of the last token of the node's source
	 * text. Returns Position.NOPOS if end positions are not generated or the
	 * position is otherwise not found.
	 * 
	 * @param tree
	 *            The tree node
	 */
	public int getEndPos(JCTree tree) {
		return Position.NOPOS;
	}

	/* ---------- parsing -------------- */

	/**
	 * Ident = IDENTIFIER
	 */
	Name ident() {
		if (S.token() == IDENTIFIER) {
			Name name = S.name();
			S.nextToken();
			return name;
		} else if (S.token() == ASSERT) {
			if (allowAsserts) {
				log.error(S.pos(), "assert.as.identifier");
				S.nextToken();
				return names.error;
			} else {
				log.warning(S.pos(), "assert.as.identifier");
				Name name = S.name();
				S.nextToken();
				return name;
			}
		} else if (S.token() == ENUM) {
			if (allowEnums) {
				log.error(S.pos(), "enum.as.identifier");
				S.nextToken();
				return names.error;
			} else {
				log.warning(S.pos(), "enum.as.identifier");
				Name name = S.name();
				S.nextToken();
				return name;
			}
		} else {
			accept(IDENTIFIER);
			return names.error;
		}
	}

	/**
	 * Qualident = Ident { DOT Ident }
	 */
	public JCExpression qualident() {
		JCExpression t = toP(F.at(S.pos()).Ident(ident()));
		while (S.token() == DOT) {
			int pos = S.pos();
			S.nextToken();
			t = toP(F.at(pos).Select(t, ident()));
		}
		return t;
	}

	/**
	 * Literal = INTLITERAL | LONGLITERAL | FLOATLITERAL | DOUBLELITERAL |
	 * CHARLITERAL | STRINGLITERAL | TRUE | FALSE | NULL
	 */
	JCExpression literal(Name prefix) {
		int pos = S.pos();
		JCExpression t = errorTree;
		switch (S.token()) {
		case INTLITERAL:
			try {
				t = F.at(pos).Literal(TypeTags.INT,
						Convert.string2int(strval(prefix), S.radix()));
			} catch (NumberFormatException ex) {
				log.error(S.pos(), "int.number.too.large", strval(prefix));
			}
			break;
		case LONGLITERAL:
			try {
				t = F.at(pos)
						.Literal(
								TypeTags.LONG,
								new Long(Convert.string2long(strval(prefix),
										S.radix())));
			} catch (NumberFormatException ex) {
				log.error(S.pos(), "int.number.too.large", strval(prefix));
			}
			break;
		case FLOATLITERAL: {
			String proper = (S.radix() == 16 ? ("0x" + S.stringVal()) : S
					.stringVal());
			Float n;
			try {
				n = Float.valueOf(proper);
			} catch (NumberFormatException ex) {
				// error already repoted in scanner
				n = Float.NaN;
			}
			if (n.floatValue() == 0.0f && !isZero(proper))
				log.error(S.pos(), "fp.number.too.small");
			else if (n.floatValue() == Float.POSITIVE_INFINITY)
				log.error(S.pos(), "fp.number.too.large");
			else
				t = F.at(pos).Literal(TypeTags.FLOAT, n);
			break;
		}
		case DOUBLELITERAL: {
			String proper = (S.radix() == 16 ? ("0x" + S.stringVal()) : S
					.stringVal());
			Double n;
			try {
				n = Double.valueOf(proper);
			} catch (NumberFormatException ex) {
				// error already reported in scanner
				n = Double.NaN;
			}
			if (n.doubleValue() == 0.0d && !isZero(proper))
				log.error(S.pos(), "fp.number.too.small");
			else if (n.doubleValue() == Double.POSITIVE_INFINITY)
				log.error(S.pos(), "fp.number.too.large");
			else
				t = F.at(pos).Literal(TypeTags.DOUBLE, n);
			break;
		}
		case CHARLITERAL:
			t = F.at(pos).Literal(TypeTags.CHAR, S.stringVal().charAt(0) + 0);
			break;
		case STRINGLITERAL:
			t = F.at(pos).Literal(TypeTags.CLASS, S.stringVal());
			break;
		case TRUE:
		case FALSE:
			t = F.at(pos)
					.Literal(TypeTags.BOOLEAN, (S.token() == TRUE ? 1 : 0));
			break;
		case NULL:
			t = F.at(pos).Literal(TypeTags.BOT, null);
			break;
		default:
			assert false;
		}
		if (t == errorTree)
			t = F.at(pos).Erroneous();
		storeEnd(t, S.endPos());
		S.nextToken();
		return t;
	}

	// where
	boolean isZero(String s) {
		char[] cs = s.toCharArray();
		int base = ((cs.length > 1 && Character.toLowerCase(cs[1]) == 'x') ? 16
				: 10);
		int i = ((base == 16) ? 2 : 0);
		while (i < cs.length && (cs[i] == '0' || cs[i] == '.'))
			i++;
		return !(i < cs.length && (Character.digit(cs[i], base) > 0));
	}

	String strval(Name prefix) {
		String s = S.stringVal();
		return (prefix.len == 0) ? s : prefix + s;
	}

	/**
	 * terms can be either expressions or types.
	 */
	public JCExpression expression() {
		return term(EXPR);
	}

	public JCExpression type() {
		return term(TYPE);
	}

	JCExpression term(int newmode) {
		int prevmode = mode;
		mode = newmode;
		JCExpression t = term();
		lastmode = mode;
		mode = prevmode;
		return t;
	}

	/**
	 * Expression = Expression1 [ExpressionRest] ExpressionRest =
	 * [AssignmentOperator Expression1] AssignmentOperator = "=" | "+=" | "-=" |
	 * "*=" | "/=" | "&=" | "|=" | "^=" | "%=" | "<<=" | ">>=" | ">>>=" Type =
	 * Type1 TypeNoParams = TypeNoParams1 StatementExpression = Expression
	 * ConstantExpression = Expression
	 */
	JCExpression term() {
		JCExpression t = term1();
		if ((mode & EXPR) != 0 && S.token() == EQ
				|| PLUSEQ.compareTo(S.token()) <= 0
				&& S.token().compareTo(GTGTGTEQ) <= 0)
			return termRest(t);
		else
			return t;
	}

	JCExpression termRest(JCExpression t) {
		switch (S.token()) {
		case EQ: {
			int pos = S.pos();
			S.nextToken();
			mode = EXPR;
			JCExpression t1 = term();
			return toP(F.at(pos).Assign(t, t1));
		}
		case PLUSEQ:
		case SUBEQ:
		case STAREQ:
		case SLASHEQ:
		case PERCENTEQ:
		case AMPEQ:
		case BAREQ:
		case CARETEQ:
		case LTLTEQ:
		case GTGTEQ:
		case GTGTGTEQ:
			int pos = S.pos();
			Token token = S.token();
			S.nextToken();
			mode = EXPR;
			JCExpression t1 = term();
			return F.at(pos).Assignop(optag(token), t, t1);
		default:
			return t;
		}
	}

	/**
	 * Expression1 = Expression2 [Expression1Rest] Type1 = Type2 TypeNoParams1 =
	 * TypeNoParams2
	 */
	JCExpression term1() {
		JCExpression t = term2();
		if ((mode & EXPR) != 0 && S.token() == QUES) {
			mode = EXPR;
			return term1Rest(t);
		} else {
			return t;
		}
	}

	/**
	 * Expression1Rest = ["?" Expression ":" Expression1]
	 */
	JCExpression term1Rest(JCExpression t) {
		if (S.token() == QUES) {
			int pos = S.pos();
			S.nextToken();
			JCExpression t1 = term();
			accept(COLON);
			JCExpression t2 = term1();
			return F.at(pos).Conditional(t, t1, t2);
		} else {
			return t;
		}
	}

	/**
	 * Expression2 = Expression3 [Expression2Rest] Type2 = Type3 TypeNoParams2 =
	 * TypeNoParams3
	 */
	JCExpression term2() {
		JCExpression t = term3();
		if ((mode & EXPR) != 0 && prec(S.token()) >= TreeInfo.orPrec) {
			mode = EXPR;
			return term2Rest(t, TreeInfo.orPrec);
		} else {
			return t;
		}
	}

	/*
	 * Expression2Rest = {infixop Expression3} | Expression3 instanceof Type
	 * infixop = "||" | "&&" | "|" | "^" | "&" | "==" | "!=" | "<" | ">" | "<="
	 * | ">=" | "<<" | ">>" | ">>>" | "+" | "-" | "*" | "/" | "%"
	 */
	JCExpression term2Rest(JCExpression t, int minprec) {
		List<JCExpression[]> savedOd = odStackSupply.elems;
		JCExpression[] odStack = newOdStack();
		List<Token[]> savedOp = opStackSupply.elems;
		Token[] opStack = newOpStack();
		// optimization, was odStack = new Tree[...]; opStack = new Tree[...];
		int top = 0;
		odStack[0] = t;
		int startPos = S.pos();
		Token topOp = ERROR;
		while (prec(S.token()) >= minprec) {
			opStack[top] = topOp;
			top++;
			topOp = S.token();
			int pos = S.pos();
			S.nextToken();
			odStack[top] = topOp == INSTANCEOF ? type() : term3();
			while (top > 0 && prec(topOp) >= prec(S.token())) {
				odStack[top - 1] = makeOp(pos, topOp, odStack[top - 1],
						odStack[top]);
				top--;
				topOp = opStack[top];
			}
		}
		assert top == 0;
		t = odStack[0];

		if (t.getTag() == JCTree.PLUS) {
			StringBuffer buf = foldStrings(t);
			if (buf != null) {
				t = toP(F.at(startPos).Literal(TypeTags.CLASS, buf.toString()));
			}
		}

		odStackSupply.elems = savedOd; // optimization
		opStackSupply.elems = savedOp; // optimization
		return t;
	}

	// where
	/**
	 * Construct a binary or type test node.
	 */
	private JCExpression makeOp(int pos, Token topOp, JCExpression od1,
			JCExpression od2) {
		if (topOp == INSTANCEOF) {
			return F.at(pos).TypeTest(od1, od2);
		} else {
			return F.at(pos).Binary(optag(topOp), od1, od2);
		}
	}

	/**
	 * If tree is a concatenation of string literals, replace it by a single
	 * literal representing the concatenated string.
	 */
	protected StringBuffer foldStrings(JCTree tree) {
		List<String> buf = List.nil();
		while (true) {
			if (tree.getTag() == JCTree.LITERAL) {
				JCLiteral lit = (JCLiteral) tree;
				if (lit.typetag == TypeTags.CLASS) {
					StringBuffer sbuf = new StringBuffer((String) lit.value);
					while (buf.nonEmpty()) {
						sbuf.append(buf.head);
						buf = buf.tail;
					}
					return sbuf;
				}
			} else if (tree.getTag() == JCTree.PLUS) {
				JCBinary op = (JCBinary) tree;
				if (op.rhs.getTag() == JCTree.LITERAL) {
					JCLiteral lit = (JCLiteral) op.rhs;
					if (lit.typetag == TypeTags.CLASS) {
						buf = buf.prepend((String) lit.value);
						tree = op.lhs;
						continue;
					}
				}
			}
			return null;
		}
	}

	/**
	 * optimization: To save allocating a new operand/operator stack for every
	 * binary operation, we use supplys.
	 */
	ListBuffer<JCExpression[]> odStackSupply = new ListBuffer<JCExpression[]>();
	ListBuffer<Token[]> opStackSupply = new ListBuffer<Token[]>();

	private JCExpression[] newOdStack() {
		if (odStackSupply.elems == odStackSupply.last)
			odStackSupply.append(new JCExpression[infixPrecedenceLevels + 1]);
		JCExpression[] odStack = odStackSupply.elems.head;
		odStackSupply.elems = odStackSupply.elems.tail;
		return odStack;
	}

	private Token[] newOpStack() {
		if (opStackSupply.elems == opStackSupply.last)
			opStackSupply.append(new Token[infixPrecedenceLevels + 1]);
		Token[] opStack = opStackSupply.elems.head;
		opStackSupply.elems = opStackSupply.elems.tail;
		return opStack;
	}

	/**
	 * Expression3 = PrefixOp Expression3 | "(" Expr | TypeNoParams ")"
	 * Expression3 | Primary {Selector} {PostfixOp} Primary = "(" Expression ")"
	 * | Literal | [TypeArguments] THIS [Arguments] | [TypeArguments] SUPER
	 * SuperSuffix | NEW [TypeArguments] Creator | Ident { "." Ident } [ "[" (
	 * "]" BracketsOpt "." CLASS | Expression "]" ) | Arguments | "." ( CLASS |
	 * THIS | [TypeArguments] SUPER Arguments | NEW [TypeArguments] InnerCreator
	 * ) ] | BasicType BracketsOpt "." CLASS PrefixOp = "++" | "--" | "!" | "~"
	 * | "+" | "-" PostfixOp = "++" | "--" Type3 = Ident { "." Ident }
	 * [TypeArguments] {TypeSelector} BracketsOpt | BasicType TypeNoParams3 =
	 * Ident { "." Ident } BracketsOpt Selector = "." [TypeArguments] Ident
	 * [Arguments] | "." THIS | "." [TypeArguments] SUPER SuperSuffix | "." NEW
	 * [TypeArguments] InnerCreator | "[" Expression "]" TypeSelector = "."
	 * Ident [TypeArguments] SuperSuffix = Arguments | "." Ident [Arguments]
	 */
	@SuppressWarnings("incomplete-switch")
	protected JCExpression term3() {
		int pos = S.pos();
		JCExpression t;
		List<JCExpression> typeArgs = typeArgumentsOpt(EXPR);
		switch (S.token()) {
		case QUES:
			if ((mode & TYPE) != 0 && (mode & (TYPEARG | NOPARAMS)) == TYPEARG) {
				mode = TYPE;
				return typeArgument();
			} else
				return illegal();
		case PLUSPLUS:
		case SUBSUB:
		case BANG:
		case TILDE:
		case PLUS:
		case SUB:
			if (typeArgs == null && (mode & EXPR) != 0) {
				Token token = S.token();
				S.nextToken();
				mode = EXPR;
				if (token == SUB
						&& (S.token() == INTLITERAL || S.token() == LONGLITERAL)
						&& S.radix() == 10) {
					mode = EXPR;
					t = literal(names.hyphen);
				} else {
					t = term3();
					return F.at(pos).Unary(unoptag(token), t);
				}
			} else
				return illegal();
			break;
		case LPAREN:
			if (typeArgs == null && (mode & EXPR) != 0) {
				S.nextToken();
				mode = EXPR | TYPE | NOPARAMS;
				t = term3();
				if ((mode & TYPE) != 0 && S.token() == LT) {
					// Could be a cast to a parameterized type
					int op = JCTree.LT;
					int pos1 = S.pos();
					S.nextToken();
					mode &= (EXPR | TYPE);
					mode |= TYPEARG;
					JCExpression t1 = term3();
					if ((mode & TYPE) != 0
							&& (S.token() == COMMA || S.token() == GT)) {
						mode = TYPE;
						ListBuffer<JCExpression> args = new ListBuffer<JCExpression>();
						args.append(t1);
						while (S.token() == COMMA) {
							S.nextToken();
							args.append(typeArgument());
						}
						accept(GT);
						t = F.at(pos1).TypeApply(t, args.toList());
						checkGenerics();
						t = bracketsOpt(toP(t));
					} else if ((mode & EXPR) != 0) {
						mode = EXPR;
						t = F.at(pos1).Binary(op, t,
								term2Rest(t1, TreeInfo.shiftPrec));
						t = termRest(term1Rest(term2Rest(t, TreeInfo.orPrec)));
					} else {
						accept(GT);
					}
				} else {
					t = termRest(term1Rest(term2Rest(t, TreeInfo.orPrec)));
				}
				accept(RPAREN);
				lastmode = mode;
				mode = EXPR;
				if ((lastmode & EXPR) == 0) {
					JCExpression t1 = term3();
					return F.at(pos).TypeCast(t, t1);
				} else if ((lastmode & TYPE) != 0) {
					switch (S.token()) {
					/* case PLUSPLUS: case SUBSUB: */
					case BANG:
					case TILDE:
					case LPAREN:
					case THIS:
					case SUPER:
					case INTLITERAL:
					case LONGLITERAL:
					case FLOATLITERAL:
					case DOUBLELITERAL:
					case CHARLITERAL:
					case STRINGLITERAL:
					case TRUE:
					case FALSE:
					case NULL:
					case NEW:
					case IDENTIFIER:
					case ASSERT:
					case ENUM:
					case BYTE:
					case SHORT:
					case CHAR:
					case INT:
					case LONG:
					case FLOAT:
					case DOUBLE:
					case BOOLEAN:
					case VOID:
						JCExpression t1 = term3();
						return F.at(pos).TypeCast(t, t1);
					}
				}
			} else
				return illegal();
			t = toP(F.at(pos).Parens(t));
			break;
		case THIS:
			if ((mode & EXPR) != 0) {
				mode = EXPR;
				t = to(F.at(pos).Ident(names._this));
				S.nextToken();
				if (typeArgs == null)
					t = argumentsOpt(null, t);
				else
					t = arguments(typeArgs, t);
				typeArgs = null;
			} else
				return illegal();
			break;
		case SUPER:
			if ((mode & EXPR) != 0) {
				mode = EXPR;
				t = to(superSuffix(typeArgs, F.at(pos).Ident(names._super)));
				typeArgs = null;
			} else
				return illegal();
			break;
		case INTLITERAL:
		case LONGLITERAL:
		case FLOATLITERAL:
		case DOUBLELITERAL:
		case CHARLITERAL:
		case STRINGLITERAL:
		case TRUE:
		case FALSE:
		case NULL:
			if (typeArgs == null && (mode & EXPR) != 0) {
				mode = EXPR;
				t = literal(names.empty);
			} else
				return illegal();
			break;
		case NEW:
			if (typeArgs != null)
				return illegal();
			if ((mode & EXPR) != 0) {
				mode = EXPR;
				S.nextToken();
				if (S.token() == LT)
					typeArgs = typeArguments();
				t = creator(pos, typeArgs);
				typeArgs = null;
			} else
				return illegal();
			break;
		case IDENTIFIER:
		case ASSERT:
		case ENUM:
			if (typeArgs != null)
				return illegal();
			t = toP(F.at(S.pos()).Ident(ident()));
			loop: while (true) {
				pos = S.pos();
				switch (S.token()) {
				case LBRACKET:
					S.nextToken();
					if (S.token() == RBRACKET) {
						S.nextToken();
						t = bracketsOpt(t);
						t = toP(F.at(pos).TypeArray(t));
						t = bracketsSuffix(t);
					} else {
						if ((mode & EXPR) != 0) {
							mode = EXPR;
							JCExpression t1 = term();
							t = to(F.at(pos).Indexed(t, t1));
						}
						accept(RBRACKET);
					}
					break loop;
				case LPAREN:
					if ((mode & EXPR) != 0) {
						mode = EXPR;
						t = arguments(typeArgs, t);
						typeArgs = null;
					}
					break loop;
				case DOT:
					S.nextToken();
					typeArgs = typeArgumentsOpt(EXPR);
					if ((mode & EXPR) != 0) {
						switch (S.token()) {
						case CLASS:
							if (typeArgs != null)
								return illegal();
							mode = EXPR;
							t = to(F.at(pos).Select(t, names._class));
							S.nextToken();
							break loop;
						case THIS:
							if (typeArgs != null)
								return illegal();
							mode = EXPR;
							t = to(F.at(pos).Select(t, names._this));
							S.nextToken();
							break loop;
						case SUPER:
							mode = EXPR;
							t = to(F.at(pos).Select(t, names._super));
							t = superSuffix(typeArgs, t);
							typeArgs = null;
							break loop;
						case NEW:
							if (typeArgs != null)
								return illegal();
							mode = EXPR;
							int pos1 = S.pos();
							S.nextToken();
							if (S.token() == LT)
								typeArgs = typeArguments();
							t = innerCreator(pos1, typeArgs, t);
							typeArgs = null;
							break loop;
						}
					}
					// typeArgs saved for next loop iteration.
					t = toP(F.at(pos).Select(t, ident()));
					break;
				default:
					break loop;
				}
			}
			if (typeArgs != null)
				illegal();
			t = typeArgumentsOpt(t);
			break;
		case BYTE:
		case SHORT:
		case CHAR:
		case INT:
		case LONG:
		case FLOAT:
		case DOUBLE:
		case BOOLEAN:
			if (typeArgs != null)
				illegal();
			t = bracketsSuffix(bracketsOpt(basicType()));
			break;
		case VOID:
			if (typeArgs != null)
				illegal();
			if ((mode & EXPR) != 0) {
				S.nextToken();
				if (S.token() == DOT) {
					JCPrimitiveTypeTree ti = toP(F.at(pos).TypeIdent(
							TypeTags.VOID));
					t = bracketsSuffix(ti);
				} else {
					return illegal(pos);
				}
			} else {
				return illegal();
			}
			break;
		default:
			return illegal();
		}
		if (typeArgs != null)
			illegal();
		while (true) {
			int pos1 = S.pos();
			if (S.token() == LBRACKET) {
				S.nextToken();
				if ((mode & TYPE) != 0) {
					int oldmode = mode;
					mode = TYPE;
					if (S.token() == RBRACKET) {
						S.nextToken();
						t = bracketsOpt(t);
						t = toP(F.at(pos1).TypeArray(t));
						return t;
					}
					mode = oldmode;
				}
				if ((mode & EXPR) != 0) {
					mode = EXPR;
					JCExpression t1 = term();
					t = to(F.at(pos1).Indexed(t, t1));
				}
				accept(RBRACKET);
			} else if (S.token() == DOT) {
				S.nextToken();
				typeArgs = typeArgumentsOpt(EXPR);
				if (S.token() == SUPER && (mode & EXPR) != 0) {
					mode = EXPR;
					t = to(F.at(pos1).Select(t, names._super));
					S.nextToken();
					t = arguments(typeArgs, t);
					typeArgs = null;
				} else if (S.token() == NEW && (mode & EXPR) != 0) {
					if (typeArgs != null)
						return illegal();
					mode = EXPR;
					int pos2 = S.pos();
					S.nextToken();
					if (S.token() == LT)
						typeArgs = typeArguments();
					t = innerCreator(pos2, typeArgs, t);
					typeArgs = null;
				} else {
					t = toP(F.at(pos1).Select(t, ident()));
					t = argumentsOpt(typeArgs, typeArgumentsOpt(t));
					typeArgs = null;
				}
			} else {
				break;
			}
		}
		while ((S.token() == PLUSPLUS || S.token() == SUBSUB)
				&& (mode & EXPR) != 0) {
			mode = EXPR;
			t = to(F.at(S.pos()).Unary(
					S.token() == PLUSPLUS ? JCTree.POSTINC : JCTree.POSTDEC, t));
			S.nextToken();
		}
		return toP(t);
	}

	/**
	 * SuperSuffix = Arguments | "." [TypeArguments] Ident [Arguments]
	 */
	JCExpression superSuffix(List<JCExpression> typeArgs, JCExpression t) {
		S.nextToken();
		if (S.token() == LPAREN || typeArgs != null) {
			t = arguments(typeArgs, t);
		} else {
			int pos = S.pos();
			accept(DOT);
			typeArgs = (S.token() == LT) ? typeArguments() : null;
			t = toP(F.at(pos).Select(t, ident()));
			t = argumentsOpt(typeArgs, t);
		}
		return t;
	}

	/**
	 * BasicType = BYTE | SHORT | CHAR | INT | LONG | FLOAT | DOUBLE | BOOLEAN
	 */
	JCPrimitiveTypeTree basicType() {
		JCPrimitiveTypeTree t = to(F.at(S.pos()).TypeIdent(typetag(S.token())));
		S.nextToken();
		return t;
	}

	/**
	 * ArgumentsOpt = [ Arguments ]
	 */
	JCExpression argumentsOpt(List<JCExpression> typeArgs, JCExpression t) {
		if ((mode & EXPR) != 0 && S.token() == LPAREN || typeArgs != null) {
			mode = EXPR;
			return arguments(typeArgs, t);
		} else {
			return t;
		}
	}

	/**
	 * Arguments = "(" [Expression { COMMA Expression }] ")"
	 */
	List<JCExpression> arguments() {
		ListBuffer<JCExpression> args = lb();
		if (S.token() == LPAREN) {
			S.nextToken();
			if (S.token() != RPAREN) {
				args.append(expression());
				while (S.token() == COMMA) {
					S.nextToken();
					args.append(expression());
				}
			}
			accept(RPAREN);
		} else {
			syntaxError(S.pos(), "expected", keywords.token2string(LPAREN));
		}
		return args.toList();
	}

	JCMethodInvocation arguments(List<JCExpression> typeArgs, JCExpression t) {
		int pos = S.pos();
		List<JCExpression> args = arguments();
		return toP(F.at(pos).Apply(typeArgs, t, args));
	}

	/**
	 * TypeArgumentsOpt = [ TypeArguments ]
	 */
	JCExpression typeArgumentsOpt(JCExpression t) {
		if (S.token() == LT && (mode & TYPE) != 0 && (mode & NOPARAMS) == 0) {
			mode = TYPE;
			checkGenerics();
			return typeArguments(t);
		} else {
			return t;
		}
	}

	List<JCExpression> typeArgumentsOpt() {
		return typeArgumentsOpt(TYPE);
	}

	List<JCExpression> typeArgumentsOpt(int useMode) {
		if (S.token() == LT) {
			checkGenerics();
			if ((mode & useMode) == 0 || (mode & NOPARAMS) != 0) {
				illegal();
			}
			mode = useMode;
			return typeArguments();
		}
		return null;
	}

	/**
	 * TypeArguments = "<" TypeArgument {"," TypeArgument} ">"
	 */
	List<JCExpression> typeArguments() {
		ListBuffer<JCExpression> args = lb();
		if (S.token() == LT) {
			S.nextToken();
			args.append(((mode & EXPR) == 0) ? typeArgument() : type());
			while (S.token() == COMMA) {
				S.nextToken();
				args.append(((mode & EXPR) == 0) ? typeArgument() : type());
			}
			switch (S.token()) {
			case GTGTGTEQ:
				S.token(GTGTEQ);
				break;
			case GTGTEQ:
				S.token(GTEQ);
				break;
			case GTEQ:
				S.token(EQ);
				break;
			case GTGTGT:
				S.token(GTGT);
				break;
			case GTGT:
				S.token(GT);
				break;
			default:
				accept(GT);
				break;
			}
		} else {
			syntaxError(S.pos(), "expected", keywords.token2string(LT));
		}
		return args.toList();
	}

	/**
	 * TypeArgument = Type | "?" | "?" EXTENDS Type {"&" Type} | "?" SUPER Type
	 */
	JCExpression typeArgument() {
		if (S.token() != QUES)
			return type();
		int pos = S.pos();
		S.nextToken();
		if (S.token() == EXTENDS) {
			TypeBoundKind t = to(F.at(S.pos()).TypeBoundKind(BoundKind.EXTENDS));
			S.nextToken();
			return F.at(pos).Wildcard(t, type());
		} else if (S.token() == SUPER) {
			TypeBoundKind t = to(F.at(S.pos()).TypeBoundKind(BoundKind.SUPER));
			S.nextToken();
			return F.at(pos).Wildcard(t, type());
		} else if (S.token() == IDENTIFIER) {
			// error recovery
			reportSyntaxError(S.prevEndPos(), "expected3",
					keywords.token2string(GT), keywords.token2string(EXTENDS),
					keywords.token2string(SUPER));
			TypeBoundKind t = F.at(Position.NOPOS).TypeBoundKind(
					BoundKind.UNBOUND);
			JCExpression wc = toP(F.at(pos).Wildcard(t, null));
			JCIdent id = toP(F.at(S.pos()).Ident(ident()));
			return F.at(pos).Erroneous(List.<JCTree> of(wc, id));
		} else {
			TypeBoundKind t = F.at(Position.NOPOS).TypeBoundKind(
					BoundKind.UNBOUND);
			return toP(F.at(pos).Wildcard(t, null));
		}
	}

	JCTypeApply typeArguments(JCExpression t) {
		int pos = S.pos();
		List<JCExpression> args = typeArguments();
		return toP(F.at(pos).TypeApply(t, args));
	}

	/**
	 * BracketsOpt = {"[" "]"}
	 */
	private JCExpression bracketsOpt(JCExpression t) {
		if (S.token() == LBRACKET) {
			int pos = S.pos();
			S.nextToken();
			t = bracketsOptCont(t, pos);
			F.at(pos);
		}
		return t;
	}

	private JCArrayTypeTree bracketsOptCont(JCExpression t, int pos) {
		accept(RBRACKET);
		t = bracketsOpt(t);
		return toP(F.at(pos).TypeArray(t));
	}

	/**
	 * BracketsSuffixExpr = "." CLASS BracketsSuffixType =
	 */
	JCExpression bracketsSuffix(JCExpression t) {
		if ((mode & EXPR) != 0 && S.token() == DOT) {
			mode = EXPR;
			int pos = S.pos();
			S.nextToken();
			accept(CLASS);
			if (S.pos() == errorEndPos) {
				// error recovery
				Name name = null;
				if (S.token() == IDENTIFIER) {
					name = S.name();
					S.nextToken();
				} else {
					name = names.error;
				}
				t = F.at(pos).Erroneous(
						List.<JCTree> of(toP(F.at(pos).Select(t, name))));
			} else {
				t = toP(F.at(pos).Select(t, names._class));
			}
		} else if ((mode & TYPE) != 0) {
			mode = TYPE;
		} else {
			syntaxError(S.pos(), "dot.class.expected");
		}
		return t;
	}

	/**
	 * Creator = Qualident [TypeArguments] ( ArrayCreatorRest | ClassCreatorRest
	 * )
	 */
	JCExpression creator(int newpos, List<JCExpression> typeArgs) {
		switch (S.token()) {
		case BYTE:
		case SHORT:
		case CHAR:
		case INT:
		case LONG:
		case FLOAT:
		case DOUBLE:
		case BOOLEAN:
			if (typeArgs == null)
				return arrayCreatorRest(newpos, basicType());
			break;
		default:
		}
		JCExpression t = qualident();
		int oldmode = mode;
		mode = TYPE;
		if (S.token() == LT) {
			checkGenerics();
			t = typeArguments(t);
		}
		while (S.token() == DOT) {
			int pos = S.pos();
			S.nextToken();
			t = toP(F.at(pos).Select(t, ident()));
			if (S.token() == LT) {
				checkGenerics();
				t = typeArguments(t);
			}
		}
		mode = oldmode;
		if (S.token() == LBRACKET) {
			JCExpression e = arrayCreatorRest(newpos, t);
			if (typeArgs != null) {
				int pos = newpos;
				if (!typeArgs.isEmpty() && typeArgs.head.pos != Position.NOPOS) {
					// note: this should always happen but we should
					// not rely on this as the parser is continuously
					// modified to improve error recovery.
					pos = typeArgs.head.pos;
				}
				setErrorEndPos(S.prevEndPos());
				reportSyntaxError(pos,
						"cannot.create.array.with.type.arguments");
				return toP(F.at(newpos).Erroneous(typeArgs.prepend(e)));
			}
			return e;
		} else if (S.token() == LPAREN) {
			return classCreatorRest(newpos, null, typeArgs, t);
		} else {
			reportSyntaxError(S.pos(), "expected2",
					keywords.token2string(LPAREN),
					keywords.token2string(LBRACKET));
			t = toP(F.at(newpos).NewClass(null, typeArgs, t,
					List.<JCExpression> nil(), null));
			return toP(F.at(newpos).Erroneous(List.<JCTree> of(t)));
		}
	}

	/**
	 * InnerCreator = Ident [TypeArguments] ClassCreatorRest
	 */
	JCExpression innerCreator(int newpos, List<JCExpression> typeArgs,
			JCExpression encl) {
		JCExpression t = toP(F.at(S.pos()).Ident(ident()));
		if (S.token() == LT) {
			checkGenerics();
			t = typeArguments(t);
		}
		return classCreatorRest(newpos, encl, typeArgs, t);
	}

	/**
	 * ArrayCreatorRest = "[" ( "]" BracketsOpt ArrayInitializer | Expression
	 * "]" {"[" Expression "]"} BracketsOpt )
	 */
	JCExpression arrayCreatorRest(int newpos, JCExpression elemtype) {
		accept(LBRACKET);
		if (S.token() == RBRACKET) {
			accept(RBRACKET);
			elemtype = bracketsOpt(elemtype);
			if (S.token() == LBRACE) {
				return arrayInitializer(newpos, elemtype);
			} else {
				return syntaxError(S.pos(), "array.dimension.missing");
			}
		} else {
			ListBuffer<JCExpression> dims = new ListBuffer<JCExpression>();
			dims.append(expression());
			accept(RBRACKET);
			while (S.token() == LBRACKET) {
				int pos = S.pos();
				S.nextToken();
				if (S.token() == RBRACKET) {
					elemtype = bracketsOptCont(elemtype, pos);
				} else {
					dims.append(expression());
					accept(RBRACKET);
				}
			}
			return toP(F.at(newpos).NewArray(elemtype, dims.toList(), null));
		}
	}

	/**
	 * ClassCreatorRest = Arguments [ClassBody]
	 */
	JCExpression classCreatorRest(int newpos, JCExpression encl,
			List<JCExpression> typeArgs, JCExpression t) {
		List<JCExpression> args = arguments();
		JCClassDecl body = null;
		if (S.token() == LBRACE) {
			int pos = S.pos();
			List<JCTree> defs = classOrInterfaceBody(names.empty, false);
			JCModifiers mods = F.at(Position.NOPOS).Modifiers(0);
			body = toP(F.at(pos).AnonymousClassDef(mods, defs));
		}
		return toP(F.at(newpos).NewClass(encl, typeArgs, t, args, body));
	}

	/**
	 * ArrayInitializer = "{" [VariableInitializer {"," VariableInitializer}]
	 * [","] "}"
	 */
	JCExpression arrayInitializer(int newpos, JCExpression t) {
		accept(LBRACE);
		ListBuffer<JCExpression> elems = new ListBuffer<JCExpression>();
		if (S.token() == COMMA) {
			S.nextToken();
		} else if (S.token() != RBRACE) {
			elems.append(variableInitializer());
			while (S.token() == COMMA) {
				S.nextToken();
				if (S.token() == RBRACE)
					break;
				elems.append(variableInitializer());
			}
		}
		accept(RBRACE);
		return toP(F.at(newpos).NewArray(t, List.<JCExpression> nil(),
				elems.toList()));
	}

	/**
	 * VariableInitializer = ArrayInitializer | Expression
	 */
	public JCExpression variableInitializer() {
		return S.token() == LBRACE ? arrayInitializer(S.pos(), null)
				: expression();
	}

	/**
	 * ParExpression = "(" Expression ")"
	 */
	JCExpression parExpression() {
		accept(LPAREN);
		JCExpression t = expression();
		accept(RPAREN);
		return t;
	}

	/**
	 * Block = "{" BlockStatements "}"
	 */
	JCBlock block(int pos, long flags) {
		accept(LBRACE);
		List<JCStatement> stats = blockStatements();
		JCBlock t = F.at(pos).Block(flags, stats);
		while (S.token() == CASE || S.token() == DEFAULT) {
			syntaxError("orphaned", keywords.token2string(S.token()));
			switchBlockStatementGroups();
		}
		// the Block node has a field "endpos" for first char of last token,
		// which is
		// usually but not necessarily the last char of the last token.
		t.endpos = S.pos();
		accept(RBRACE);
		return toP(t);
	}

	public JCBlock block() {
		return block(S.pos(), 0);
	}

	/**
	 * BlockStatements = { BlockStatement } BlockStatement =
	 * LocalVariableDeclarationStatement | ClassOrInterfaceOrEnumDeclaration |
	 * [Ident ":"] Statement LocalVariableDeclarationStatement = { FINAL | '@'
	 * Annotation } Type VariableDeclarators ";"
	 */
	@SuppressWarnings("fallthrough")
	List<JCStatement> blockStatements() {
		// todo: skip to anchor on error(?)
		int lastErrPos = -1;
		ListBuffer<JCStatement> stats = new ListBuffer<JCStatement>();
		while (true) {
			int pos = S.pos();
			switch (S.token()) {
			case RBRACE:
			case CASE:
			case DEFAULT:
			case EOF:
				return stats.toList();
			case LBRACE:
			case IF:
			case FOR:
			case WHILE:
			case DO:
			case TRY:
			case SWITCH:
			case SYNCHRONIZED:
			case RETURN:
			case THROW:
			case BREAK:
			case CONTINUE:
			case SEMI:
			case ELSE:
			case FINALLY:
			case CATCH:
				stats.append(statement());
				break;
			case MONKEYS_AT:
			case FINAL: {
				String dc = S.docComment();
				JCModifiers mods = modifiersOpt();
				if (S.token() == INTERFACE || S.token() == CLASS || allowEnums
						&& S.token() == ENUM) {
					stats.append(classOrInterfaceOrEnumDeclaration(mods, dc));
				} else {
					JCExpression t = type();
					stats.appendList(variableDeclarators(mods, t,
							new ListBuffer<JCStatement>()));
					// A "LocalVariableDeclarationStatement" subsumes the
					// terminating semicolon
					storeEnd(stats.elems.last(), S.endPos());
					accept(SEMI);
				}
				break;
			}
			case ABSTRACT:
			case STRICTFP: {
				String dc = S.docComment();
				JCModifiers mods = modifiersOpt();
				stats.append(classOrInterfaceOrEnumDeclaration(mods, dc));
				break;
			}
			case INTERFACE:
			case CLASS:
				stats.append(classOrInterfaceOrEnumDeclaration(modifiersOpt(),
						S.docComment()));
				break;
			case ENUM:
			case ASSERT:
				if (allowEnums && S.token() == ENUM) {
					log.error(S.pos(), "local.enum");
					stats.append(classOrInterfaceOrEnumDeclaration(
							modifiersOpt(), S.docComment()));
					break;
				} else if (allowAsserts && S.token() == ASSERT) {
					stats.append(statement());
					break;
				}
				/* fall through to default */
			default:
				Name name = S.name();
				JCExpression t = term(EXPR | TYPE);
				if (S.token() == COLON && t.getTag() == JCTree.IDENT) {
					S.nextToken();
					JCStatement stat = statement();
					stats.append(F.at(pos).Labelled(name, stat));
				} else if ((lastmode & TYPE) != 0
						&& (S.token() == IDENTIFIER || S.token() == ASSERT || S
								.token() == ENUM)) {
					pos = S.pos();
					JCModifiers mods = F.at(Position.NOPOS).Modifiers(0);
					F.at(pos);
					stats.appendList(variableDeclarators(mods, t,
							new ListBuffer<JCStatement>()));
					// A "LocalVariableDeclarationStatement" subsumes the
					// terminating semicolon
					storeEnd(stats.elems.last(), S.endPos());
					accept(SEMI);
				} else {
					// This Exec is an "ExpressionStatement"; it subsumes the
					// terminating semicolon
					stats.append(to(F.at(pos).Exec(checkExprStat(t))));
					accept(SEMI);
				}
			}

			// error recovery
			if (S.pos() == lastErrPos)
				return stats.toList();
			if (S.pos() <= errorEndPos) {
				skip(false, true, true, true);
				lastErrPos = S.pos();
			}

			// ensure no dangling /** @deprecated */ active
			S.resetDeprecatedFlag();
		}
	}

	/**
	 * Statement = Block | IF ParExpression Statement [ELSE Statement] | FOR "("
	 * ForInitOpt ";" [Expression] ";" ForUpdateOpt ")" Statement | FOR "("
	 * FormalParameter : Expression ")" Statement | WHILE ParExpression
	 * Statement | DO Statement WHILE ParExpression ";" | TRY Block ( Catches |
	 * [Catches] FinallyPart ) | SWITCH ParExpression "{"
	 * SwitchBlockStatementGroups "}" | SYNCHRONIZED ParExpression Block |
	 * RETURN [Expression] ";" | THROW Expression ";" | BREAK [Ident] ";" |
	 * CONTINUE [Ident] ";" | ASSERT Expression [ ":" Expression ] ";" | ";" |
	 * ExpressionStatement | Ident ":" Statement
	 */
	@SuppressWarnings("fallthrough")
	public JCStatement statement() {
		int pos = S.pos();
		switch (S.token()) {
		case LBRACE:
			return block();
		case IF: {
			S.nextToken();
			JCExpression cond = parExpression();
			JCStatement thenpart = statement();
			JCStatement elsepart = null;
			if (S.token() == ELSE) {
				S.nextToken();
				elsepart = statement();
			}
			return F.at(pos).If(cond, thenpart, elsepart);
		}
		case FOR: {
			S.nextToken();
			accept(LPAREN);
			List<JCStatement> inits = S.token() == SEMI ? List
					.<JCStatement> nil() : forInit();
			if (inits.length() == 1 && inits.head.getTag() == JCTree.VARDEF
					&& ((JCVariableDecl) inits.head).init == null
					&& S.token() == COLON) {
				checkForeach();
				JCVariableDecl var = (JCVariableDecl) inits.head;
				accept(COLON);
				JCExpression expr = expression();
				accept(RPAREN);
				JCStatement body = statement();
				return F.at(pos).ForeachLoop(var, expr, body);
			} else {
				accept(SEMI);
				JCExpression cond = S.token() == SEMI ? null : expression();
				accept(SEMI);
				List<JCExpressionStatement> steps = S.token() == RPAREN ? List
						.<JCExpressionStatement> nil() : forUpdate();
				accept(RPAREN);
				JCStatement body = statement();
				return F.at(pos).ForLoop(inits, cond, steps, body);
			}
		}
		case WHILE: {
			S.nextToken();
			JCExpression cond = parExpression();
			JCStatement body = statement();
			return F.at(pos).WhileLoop(cond, body);
		}
		case DO: {
			S.nextToken();
			JCStatement body = statement();
			accept(WHILE);
			JCExpression cond = parExpression();
			JCDoWhileLoop t = to(F.at(pos).DoLoop(body, cond));
			accept(SEMI);
			return t;
		}
		case TRY: {
			S.nextToken();
			JCBlock body = block();
			ListBuffer<JCCatch> catchers = new ListBuffer<JCCatch>();
			JCBlock finalizer = null;
			if (S.token() == CATCH || S.token() == FINALLY) {
				while (S.token() == CATCH)
					catchers.append(catchClause());
				if (S.token() == FINALLY) {
					S.nextToken();
					finalizer = block();
				}
			} else {
				log.error(pos, "try.without.catch.or.finally");
			}
			return F.at(pos).Try(body, catchers.toList(), finalizer);
		}
		case SWITCH: {
			S.nextToken();
			JCExpression selector = parExpression();
			accept(LBRACE);
			List<JCCase> cases = switchBlockStatementGroups();
			JCSwitch t = to(F.at(pos).Switch(selector, cases));
			accept(RBRACE);
			return t;
		}
		case SYNCHRONIZED: {
			S.nextToken();
			JCExpression lock = parExpression();
			JCBlock body = block();
			return F.at(pos).Synchronized(lock, body);
		}
		case RETURN: {
			S.nextToken();
			JCExpression result = S.token() == SEMI ? null : expression();
			JCReturn t = to(F.at(pos).Return(result));
			accept(SEMI);
			return t;
		}
		case THROW: {
			S.nextToken();
			JCExpression exc = expression();
			JCThrow t = to(F.at(pos).Throw(exc));
			accept(SEMI);
			return t;
		}
		case BREAK: {
			S.nextToken();
			Name label = (S.token() == IDENTIFIER || S.token() == ASSERT || S
					.token() == ENUM) ? ident() : null;
			JCBreak t = to(F.at(pos).Break(label));
			accept(SEMI);
			return t;
		}
		case CONTINUE: {
			S.nextToken();
			Name label = (S.token() == IDENTIFIER || S.token() == ASSERT || S
					.token() == ENUM) ? ident() : null;
			JCContinue t = to(F.at(pos).Continue(label));
			accept(SEMI);
			return t;
		}
		case SEMI:
			S.nextToken();
			return toP(F.at(pos).Skip());
		case ELSE:
			return toP(F.Exec(syntaxError("else.without.if")));
		case FINALLY:
			return toP(F.Exec(syntaxError("finally.without.try")));
		case CATCH:
			return toP(F.Exec(syntaxError("catch.without.try")));
		case ASSERT: {
			if (allowAsserts && S.token() == ASSERT) {
				S.nextToken();
				JCExpression assertion = expression();
				JCExpression message = null;
				if (S.token() == COLON) {
					S.nextToken();
					message = expression();
				}
				JCAssert t = to(F.at(pos).Assert(assertion, message));
				accept(SEMI);
				return t;
			}
			/* else fall through to default case */
		}
		case ENUM:
		default:
			Name name = S.name();
			JCExpression expr = expression();
			if (S.token() == COLON && expr.getTag() == JCTree.IDENT) {
				S.nextToken();
				JCStatement stat = statement();
				return F.at(pos).Labelled(name, stat);
			} else {
				// This Exec is an "ExpressionStatement"; it subsumes the
				// terminating semicolon
				JCExpressionStatement stat = to(F.at(pos).Exec(
						checkExprStat(expr)));
				accept(SEMI);
				return stat;
			}
		}
	}

	/**
	 * CatchClause = CATCH "(" FormalParameter ")" Block
	 */
	JCCatch catchClause() {
		int pos = S.pos();
		accept(CATCH);
		accept(LPAREN);
		JCVariableDecl formal = variableDeclaratorId(optFinal(Flags.PARAMETER),
				qualident());
		accept(RPAREN);
		JCBlock body = block();
		return F.at(pos).Catch(formal, body);
	}

	/**
	 * SwitchBlockStatementGroups = { SwitchBlockStatementGroup }
	 * SwitchBlockStatementGroup = SwitchLabel BlockStatements SwitchLabel =
	 * CASE ConstantExpression ":" | DEFAULT ":"
	 */
	List<JCCase> switchBlockStatementGroups() {
		ListBuffer<JCCase> cases = new ListBuffer<JCCase>();
		while (true) {
			int pos = S.pos();
			switch (S.token()) {
			case CASE: {
				S.nextToken();
				JCExpression pat = expression();
				accept(COLON);
				List<JCStatement> stats = blockStatements();
				JCCase c = F.at(pos).Case(pat, stats);
				if (stats.isEmpty())
					storeEnd(c, S.prevEndPos());
				cases.append(c);
				break;
			}
			case DEFAULT: {
				S.nextToken();
				accept(COLON);
				List<JCStatement> stats = blockStatements();
				JCCase c = F.at(pos).Case(null, stats);
				if (stats.isEmpty())
					storeEnd(c, S.prevEndPos());
				cases.append(c);
				break;
			}
			case RBRACE:
			case EOF:
				return cases.toList();
			default:
				S.nextToken(); // to ensure progress
				syntaxError(pos, "expected3", keywords.token2string(CASE),
						keywords.token2string(DEFAULT),
						keywords.token2string(RBRACE));
			}
		}
	}

	/**
	 * MoreStatementExpressions = { COMMA StatementExpression }
	 */
	<T extends ListBuffer<? super JCExpressionStatement>> T moreStatementExpressions(
			int pos, JCExpression first, T stats) {
		// This Exec is a "StatementExpression"; it subsumes no terminating
		// token
		stats.append(toP(F.at(pos).Exec(checkExprStat(first))));
		while (S.token() == COMMA) {
			S.nextToken();
			pos = S.pos();
			JCExpression t = expression();
			// This Exec is a "StatementExpression"; it subsumes no terminating
			// token
			stats.append(toP(F.at(pos).Exec(checkExprStat(t))));
		}
		return stats;
	}

	/**
	 * ForInit = StatementExpression MoreStatementExpressions | { FINAL | '@'
	 * Annotation } Type VariableDeclarators
	 */
	List<JCStatement> forInit() {
		ListBuffer<JCStatement> stats = lb();
		int pos = S.pos();
		if (S.token() == FINAL || S.token() == MONKEYS_AT) {
			return variableDeclarators(optFinal(0), type(), stats).toList();
		} else {
			JCExpression t = term(EXPR | TYPE);
			if ((lastmode & TYPE) != 0
					&& (S.token() == IDENTIFIER || S.token() == ASSERT || S
							.token() == ENUM))
				return variableDeclarators(modifiersOpt(), t, stats).toList();
			else
				return moreStatementExpressions(pos, t, stats).toList();
		}
	}

	/**
	 * ForUpdate = StatementExpression MoreStatementExpressions
	 */
	List<JCExpressionStatement> forUpdate() {
		return moreStatementExpressions(S.pos(), expression(),
				new ListBuffer<JCExpressionStatement>()).toList();
	}

	/**
	 * AnnotationsOpt = { '@' Annotation }
	 */
	List<JCAnnotation> annotationsOpt() {
		if (S.token() != MONKEYS_AT)
			return List.nil(); // optimization
		ListBuffer<JCAnnotation> buf = new ListBuffer<JCAnnotation>();
		while (S.token() == MONKEYS_AT) {
			int pos = S.pos();
			S.nextToken();
			buf.append(annotation(pos));
		}
		return buf.toList();
	}

	/**
	 * ModifiersOpt = { Modifier } Modifier = PUBLIC | PROTECTED | PRIVATE |
	 * STATIC | ABSTRACT | FINAL | NATIVE | SYNCHRONIZED | TRANSIENT | VOLATILE
	 * | "@" | "@" Annotation
	 */
	JCModifiers modifiersOpt() {
		return modifiersOpt(null);
	}

	JCModifiers modifiersOpt(JCModifiers partial) {
		long flags = (partial == null) ? 0 : partial.flags;
		if (S.deprecatedFlag()) {
			flags |= Flags.DEPRECATED;
			S.resetDeprecatedFlag();
		}
		ListBuffer<JCAnnotation> annotations = new ListBuffer<JCAnnotation>();
		if (partial != null)
			annotations.appendList(partial.annotations);
		int pos = S.pos();
		int lastPos = Position.NOPOS;
		loop: while (true) {
			long flag;
			switch (S.token()) {
			case PRIVATE:
				flag = Flags.PRIVATE;
				break;
			case PROTECTED:
				flag = Flags.PROTECTED;
				break;
			case PUBLIC:
				flag = Flags.PUBLIC;
				break;
			case STATIC:
				flag = Flags.STATIC;
				break;
			case TRANSIENT:
				flag = Flags.TRANSIENT;
				break;
			case FINAL:
				flag = Flags.FINAL;
				break;
			case ABSTRACT:
				flag = Flags.ABSTRACT;
				break;
			case NATIVE:
				flag = Flags.NATIVE;
				break;
			case VOLATILE:
				flag = Flags.VOLATILE;
				break;
			case SYNCHRONIZED:
				flag = Flags.SYNCHRONIZED;
				break;
			case STRICTFP:
				flag = Flags.STRICTFP;
				break;
			case MONKEYS_AT:
				flag = Flags.ANNOTATION;
				break;
			default:
				break loop;
			}
			if ((flags & flag) != 0)
				log.error(S.pos(), "repeated.modifier");
			lastPos = S.pos();
			S.nextToken();
			if (flag == Flags.ANNOTATION) {
				checkAnnotations();
				if (S.token() != INTERFACE) {
					JCAnnotation ann = annotation(lastPos);
					// if first modifier is an annotation, set pos to
					// annotation's.
					if (flags == 0 && annotations.isEmpty())
						pos = ann.pos;
					annotations.append(ann);
					lastPos = ann.pos;
					flag = 0;
				}
			}
			flags |= flag;
		}
		switch (S.token()) {
		case ENUM:
			flags |= Flags.ENUM;
			break;
		case INTERFACE:
			flags |= Flags.INTERFACE;
			break;
		default:
			break;
		}

		/*
		 * A modifiers tree with no modifier tokens or annotations has no text
		 * position.
		 */
		if (flags == 0 && annotations.isEmpty())
			pos = Position.NOPOS;

		JCModifiers mods = F.at(pos).Modifiers(flags, annotations.toList());
		if (pos != Position.NOPOS)
			storeEnd(mods, S.prevEndPos());
		return mods;
	}

	/**
	 * Annotation = "@" Qualident [ "(" AnnotationFieldValues ")" ]
	 * 
	 * @param pos
	 *            position of "@" token
	 */
	JCAnnotation annotation(int pos) {
		// accept(AT); // AT consumed by caller
		checkAnnotations();
		JCTree ident = qualident();
		List<JCExpression> fieldValues = annotationFieldValuesOpt();
		JCAnnotation ann = F.at(pos).Annotation(ident, fieldValues);
		storeEnd(ann, S.prevEndPos());
		return ann;
	}

	List<JCExpression> annotationFieldValuesOpt() {
		return (S.token() == LPAREN) ? annotationFieldValues() : List
				.<JCExpression> nil();
	}

	/**
	 * AnnotationFieldValues = "(" [ AnnotationFieldValue { ","
	 * AnnotationFieldValue } ] ")"
	 */
	List<JCExpression> annotationFieldValues() {
		accept(LPAREN);
		ListBuffer<JCExpression> buf = new ListBuffer<JCExpression>();
		if (S.token() != RPAREN) {
			buf.append(annotationFieldValue());
			while (S.token() == COMMA) {
				S.nextToken();
				buf.append(annotationFieldValue());
			}
		}
		accept(RPAREN);
		return buf.toList();
	}

	/**
	 * AnnotationFieldValue = AnnotationValue | Identifier "=" AnnotationValue
	 */
	JCExpression annotationFieldValue() {
		if (S.token() == IDENTIFIER) {
			mode = EXPR;
			JCExpression t1 = term1();
			if (t1.getTag() == JCTree.IDENT && S.token() == EQ) {
				int pos = S.pos();
				accept(EQ);
				return toP(F.at(pos).Assign(t1, annotationValue()));
			} else {
				return t1;
			}
		}
		return annotationValue();
	}

	/*
	 * AnnotationValue = ConditionalExpression | Annotation | "{" [
	 * AnnotationValue { "," AnnotationValue } ] [","] "}"
	 */
	JCExpression annotationValue() {
		int pos;
		switch (S.token()) {
		case MONKEYS_AT:
			pos = S.pos();
			S.nextToken();
			return annotation(pos);
		case LBRACE:
			pos = S.pos();
			accept(LBRACE);
			ListBuffer<JCExpression> buf = new ListBuffer<JCExpression>();
			if (S.token() != RBRACE) {
				buf.append(annotationValue());
				while (S.token() == COMMA) {
					S.nextToken();
					if (S.token() == RBRACE)
						break;
					buf.append(annotationValue());
				}
			}
			accept(RBRACE);
			return toP(F.at(pos).NewArray(null, List.<JCExpression> nil(),
					buf.toList()));
		default:
			mode = EXPR;
			return term1();
		}
	}

	/**
	 * VariableDeclarators = VariableDeclarator { "," VariableDeclarator }
	 */
	public <T extends ListBuffer<? super JCVariableDecl>> T variableDeclarators(
			JCModifiers mods, JCExpression type, T vdefs) {
		return variableDeclaratorsRest(S.pos(), mods, type, ident(), false,
				null, vdefs);
	}

	/**
	 * VariableDeclaratorsRest = VariableDeclaratorRest { "," VariableDeclarator
	 * } ConstantDeclaratorsRest = ConstantDeclaratorRest { ","
	 * ConstantDeclarator }
	 * 
	 * @param reqInit
	 *            Is an initializer always required?
	 * @param dc
	 *            The documentation comment for the variable declarations, or
	 *            null.
	 */
	<T extends ListBuffer<? super JCVariableDecl>> T variableDeclaratorsRest(
			int pos, JCModifiers mods, JCExpression type, Name name,
			boolean reqInit, String dc, T vdefs) {
		vdefs.append(variableDeclaratorRest(pos, mods, type, name, reqInit, dc));
		while (S.token() == COMMA) {
			// All but last of multiple declarators subsume a comma
			storeEnd((JCTree) vdefs.elems.last(), S.endPos());
			S.nextToken();
			vdefs.append(variableDeclarator(mods, type, reqInit, dc));
		}
		return vdefs;
	}

	/**
	 * VariableDeclarator = Ident VariableDeclaratorRest ConstantDeclarator =
	 * Ident ConstantDeclaratorRest
	 */
	JCVariableDecl variableDeclarator(JCModifiers mods, JCExpression type,
			boolean reqInit, String dc) {
		return variableDeclaratorRest(S.pos(), mods, type, ident(), reqInit, dc);
	}

	/**
	 * VariableDeclaratorRest = BracketsOpt ["=" VariableInitializer]
	 * ConstantDeclaratorRest = BracketsOpt "=" VariableInitializer
	 * 
	 * @param reqInit
	 *            Is an initializer always required?
	 * @param dc
	 *            The documentation comment for the variable declarations, or
	 *            null.
	 */
	JCVariableDecl variableDeclaratorRest(int pos, JCModifiers mods,
			JCExpression type, Name name, boolean reqInit, String dc) {
		type = bracketsOpt(type);
		JCExpression init = null;
		if (S.token() == EQ) {
			S.nextToken();
			init = variableInitializer();
		} else if (reqInit)
			syntaxError(S.pos(), "expected", keywords.token2string(EQ));
		JCVariableDecl result = toP(F.at(pos).VarDef(mods, name, type, init));
		attach(result, dc);
		return result;
	}

	/**
	 * VariableDeclaratorId = Ident BracketsOpt
	 */
	JCVariableDecl variableDeclaratorId(JCModifiers mods, JCExpression type) {
		int pos = S.pos();
		Name name = ident();
		if ((mods.flags & Flags.VARARGS) == 0)
			type = bracketsOpt(type);
		return toP(F.at(pos).VarDef(mods, name, type, null));
	}

	/**
	 * CompilationUnit = [ { "@" Annotation } PACKAGE Qualident ";"]
	 * {ImportDeclaration} {TypeDeclaration}
	 */
	public JCTree.JCCompilationUnit compilationUnit() {
		int pos = S.pos();
		JCExpression pid = null;
		String dc = S.docComment();
		JCModifiers mods = null;
		List<JCAnnotation> packageAnnotations = List.nil();
		if (S.token() == MONKEYS_AT)
			mods = modifiersOpt();

		if (S.token() == PACKAGE) {
			if (mods != null) {
				checkNoMods(mods.flags);
				packageAnnotations = mods.annotations;
				mods = null;
			}
			S.nextToken();
			pid = qualident();
			accept(SEMI);
		}
		ListBuffer<JCTree> defs = new ListBuffer<JCTree>();
		boolean checkForImports = true;
		while (S.token() != EOF) {
			if (S.pos() <= errorEndPos) {
				// error recovery
				skip(checkForImports, false, false, false);
				if (S.token() == EOF)
					break;
			}
			if (checkForImports && mods == null && S.token() == IMPORT) {
				defs.append(importDeclaration());
			} else {
				JCTree def = typeDeclaration(mods);
				if (def instanceof JCExpressionStatement)
					def = ((JCExpressionStatement) def).expr;
				defs.append(def);
				if (def instanceof JCClassDecl)
					checkForImports = false;
				mods = null;
			}
		}
		JCTree.JCCompilationUnit toplevel = F.at(pos).TopLevel(
				packageAnnotations, pid, defs.toList());
		attach(toplevel, dc);
		if (defs.elems.isEmpty())
			storeEnd(toplevel, S.prevEndPos());
		if (keepDocComments)
			toplevel.docComments = docComments;
		return toplevel;
	}

	/**
	 * ImportDeclaration = IMPORT [ STATIC ] Ident { "." Ident } [ "." "*" ] ";"
	 */
	JCTree importDeclaration() {
		int pos = S.pos();
		S.nextToken();
		boolean importStatic = false;
		if (S.token() == STATIC) {
			checkStaticImports();
			importStatic = true;
			S.nextToken();
		}
		JCExpression pid = toP(F.at(S.pos()).Ident(ident()));
		do {
			int pos1 = S.pos();
			accept(DOT);
			if (S.token() == STAR) {
				pid = to(F.at(pos1).Select(pid, names.asterisk));
				S.nextToken();
				break;
			} else {
				pid = toP(F.at(pos1).Select(pid, ident()));
			}
		} while (S.token() == DOT);
		accept(SEMI);
		return toP(F.at(pos).Import(pid, importStatic));
	}

	/**
	 * TypeDeclaration = ClassOrInterfaceOrEnumDeclaration | ";"
	 */
	JCTree typeDeclaration(JCModifiers mods) {
		int pos = S.pos();
		if (mods == null && S.token() == SEMI) {
			S.nextToken();
			return toP(F.at(pos).Skip());
		} else {
			String dc = S.docComment();
			return classOrInterfaceOrEnumDeclaration(modifiersOpt(mods), dc);
		}
	}

	/**
	 * ClassOrInterfaceOrEnumDeclaration = ModifiersOpt (ClassDeclaration |
	 * InterfaceDeclaration | EnumDeclaration)
	 * 
	 * @param mods
	 *            Any modifiers starting the class or interface declaration
	 * @param dc
	 *            The documentation comment for the class, or null.
	 */
	JCStatement classOrInterfaceOrEnumDeclaration(JCModifiers mods, String dc) {
		if (S.token() == CLASS) {
			return classDeclaration(mods, dc);
		} else if (S.token() == INTERFACE) {
			return interfaceDeclaration(mods, dc);
		} else if (allowEnums) {
			if (S.token() == ENUM) {
				return enumDeclaration(mods, dc);
			} else {
				int pos = S.pos();
				List<JCTree> errs;
				if (S.token() == IDENTIFIER) {
					errs = List
							.<JCTree> of(mods, toP(F.at(pos).Ident(ident())));
					setErrorEndPos(S.pos());
				} else {
					errs = List.<JCTree> of(mods);
				}
				return toP(F.Exec(syntaxError(pos, errs, "expected3",
						keywords.token2string(CLASS),
						keywords.token2string(INTERFACE),
						keywords.token2string(ENUM))));
			}
		} else {
			if (S.token() == ENUM) {
				log.error(S.pos(), "enums.not.supported.in.source", source.name);
				allowEnums = true;
				return enumDeclaration(mods, dc);
			}
			int pos = S.pos();
			List<JCTree> errs;
			if (S.token() == IDENTIFIER) {
				errs = List.<JCTree> of(mods, toP(F.at(pos).Ident(ident())));
				setErrorEndPos(S.pos());
			} else {
				errs = List.<JCTree> of(mods);
			}
			return toP(F.Exec(syntaxError(pos, errs, "expected2",
					keywords.token2string(CLASS),
					keywords.token2string(INTERFACE))));
		}
	}

	/**
	 * ClassDeclaration = CLASS Ident TypeParametersOpt [EXTENDS Type]
	 * [IMPLEMENTS TypeList] ClassBody
	 * 
	 * @param mods
	 *            The modifiers starting the class declaration
	 * @param dc
	 *            The documentation comment for the class, or null.
	 */
	JCClassDecl classDeclaration(JCModifiers mods, String dc) {
		int pos = S.pos();
		accept(CLASS);
		Name name = ident();

		List<JCTypeParameter> typarams = typeParametersOpt();

		JCTree extending = null;
		if (S.token() == EXTENDS) {
			S.nextToken();
			extending = type();
		}
		List<JCExpression> implementing = List.nil();
		if (S.token() == IMPLEMENTS) {
			S.nextToken();
			implementing = typeList();
		}
		List<JCTree> defs = classOrInterfaceBody(name, false);
		JCClassDecl result = toP(F.at(pos).ClassDef(mods, name, typarams,
				extending, implementing, defs));
		attach(result, dc);
		return result;
	}

	/**
	 * InterfaceDeclaration = INTERFACE Ident TypeParametersOpt [EXTENDS
	 * TypeList] InterfaceBody
	 * 
	 * @param mods
	 *            The modifiers starting the interface declaration
	 * @param dc
	 *            The documentation comment for the interface, or null.
	 */
	JCClassDecl interfaceDeclaration(JCModifiers mods, String dc) {
		int pos = S.pos();
		accept(INTERFACE);
		Name name = ident();

		List<JCTypeParameter> typarams = typeParametersOpt();

		List<JCExpression> extending = List.nil();
		if (S.token() == EXTENDS) {
			S.nextToken();
			extending = typeList();
		}
		List<JCTree> defs = classOrInterfaceBody(name, true);
		JCClassDecl result = toP(F.at(pos).ClassDef(mods, name, typarams, null,
				extending, defs));
		attach(result, dc);
		return result;
	}

	/**
	 * EnumDeclaration = ENUM Ident [IMPLEMENTS TypeList] EnumBody
	 * 
	 * @param mods
	 *            The modifiers starting the enum declaration
	 * @param dc
	 *            The documentation comment for the enum, or null.
	 */
	JCClassDecl enumDeclaration(JCModifiers mods, String dc) {
		int pos = S.pos();
		accept(ENUM);
		Name name = ident();

		List<JCExpression> implementing = List.nil();
		if (S.token() == IMPLEMENTS) {
			S.nextToken();
			implementing = typeList();
		}

		List<JCTree> defs = enumBody(name);
		JCModifiers newMods = F.at(mods.pos).Modifiers(mods.flags | Flags.ENUM,
				mods.annotations);
		JCClassDecl result = toP(F.at(pos).ClassDef(newMods, name,
				List.<JCTypeParameter> nil(), null, implementing, defs));
		attach(result, dc);
		return result;
	}

	/**
	 * EnumBody = "{" { EnumeratorDeclarationList } [","] [ ";"
	 * {ClassBodyDeclaration} ] "}"
	 */
	List<JCTree> enumBody(Name enumName) {
		accept(LBRACE);
		ListBuffer<JCTree> defs = new ListBuffer<JCTree>();
		if (S.token() == COMMA) {
			S.nextToken();
		} else if (S.token() != RBRACE && S.token() != SEMI) {
			defs.append(enumeratorDeclaration(enumName));
			while (S.token() == COMMA) {
				S.nextToken();
				if (S.token() == RBRACE || S.token() == SEMI)
					break;
				defs.append(enumeratorDeclaration(enumName));
			}
			if (S.token() != SEMI && S.token() != RBRACE) {
				defs.append(syntaxError(S.pos(), "expected3",
						keywords.token2string(COMMA),
						keywords.token2string(RBRACE),
						keywords.token2string(SEMI)));
				S.nextToken();
			}
		}
		if (S.token() == SEMI) {
			S.nextToken();
			while (S.token() != RBRACE && S.token() != EOF) {
				defs.appendList(classOrInterfaceBodyDeclaration(enumName, false));
				if (S.pos() <= errorEndPos) {
					// error recovery
					skip(false, true, true, false);
				}
			}
		}
		accept(RBRACE);
		return defs.toList();
	}

	/**
	 * EnumeratorDeclaration = AnnotationsOpt [TypeArguments] IDENTIFIER [
	 * Arguments ] [ "{" ClassBody "}" ]
	 */
	JCTree enumeratorDeclaration(Name enumName) {
		String dc = S.docComment();
		int flags = Flags.PUBLIC | Flags.STATIC | Flags.FINAL | Flags.ENUM;
		if (S.deprecatedFlag()) {
			flags |= Flags.DEPRECATED;
			S.resetDeprecatedFlag();
		}
		int pos = S.pos();
		List<JCAnnotation> annotations = annotationsOpt();
		JCModifiers mods = F.at(annotations.isEmpty() ? Position.NOPOS : pos)
				.Modifiers(flags, annotations);
		List<JCExpression> typeArgs = typeArgumentsOpt();
		int identPos = S.pos();
		Name name = ident();
		int createPos = S.pos();
		List<JCExpression> args = (S.token() == LPAREN) ? arguments() : List
				.<JCExpression> nil();
		JCClassDecl body = null;
		if (S.token() == LBRACE) {
			JCModifiers mods1 = F.at(Position.NOPOS).Modifiers(
					Flags.ENUM | Flags.STATIC);
			List<JCTree> defs = classOrInterfaceBody(names.empty, false);
			body = toP(F.at(identPos).AnonymousClassDef(mods1, defs));
		}
		if (args.isEmpty() && body == null)
			createPos = Position.NOPOS;
		JCIdent ident = F.at(Position.NOPOS).Ident(enumName);
		JCNewClass create = F.at(createPos).NewClass(null, typeArgs, ident,
				args, body);
		if (createPos != Position.NOPOS)
			storeEnd(create, S.prevEndPos());
		ident = F.at(Position.NOPOS).Ident(enumName);
		JCTree result = toP(F.at(pos).VarDef(mods, name, ident, create));
		attach(result, dc);
		return result;
	}

	/**
	 * TypeList = Type {"," Type}
	 */
	List<JCExpression> typeList() {
		ListBuffer<JCExpression> ts = new ListBuffer<JCExpression>();
		ts.append(type());
		while (S.token() == COMMA) {
			S.nextToken();
			ts.append(type());
		}
		return ts.toList();
	}

	/**
	 * ClassBody = "{" {ClassBodyDeclaration} "}" InterfaceBody = "{"
	 * {InterfaceBodyDeclaration} "}"
	 */
	List<JCTree> classOrInterfaceBody(Name className, boolean isInterface) {
		accept(LBRACE);
		if (S.pos() <= errorEndPos) {
			// error recovery
			skip(false, true, false, false);
			if (S.token() == LBRACE)
				S.nextToken();
		}
		ListBuffer<JCTree> defs = new ListBuffer<JCTree>();
		while (S.token() != RBRACE && S.token() != EOF) {
			defs.appendList(classOrInterfaceBodyDeclaration(className,
					isInterface));
			if (S.pos() <= errorEndPos) {
				// error recovery
				skip(false, true, true, false);
			}
		}
		accept(RBRACE);
		return defs.toList();
	}

	/**
	 * ClassBodyDeclaration = ";" | [STATIC] Block | ModifiersOpt ( Type Ident (
	 * VariableDeclaratorsRest ";" | MethodDeclaratorRest ) | VOID Ident
	 * MethodDeclaratorRest | TypeParameters (Type | VOID) Ident
	 * MethodDeclaratorRest | Ident ConstructorDeclaratorRest | TypeParameters
	 * Ident ConstructorDeclaratorRest | ClassOrInterfaceOrEnumDeclaration )
	 * InterfaceBodyDeclaration = ";" | ModifiersOpt Type Ident (
	 * ConstantDeclaratorsRest | InterfaceMethodDeclaratorRest ";" )
	 */
	List<JCTree> classOrInterfaceBodyDeclaration(Name className,
			boolean isInterface) {
		if (S.token() == SEMI) {
			S.nextToken();
			return List.<JCTree> of(F.at(Position.NOPOS).Block(0,
					List.<JCStatement> nil()));
		} else {
			String dc = S.docComment();
			int pos = S.pos();
			JCModifiers mods = modifiersOpt();
			if (S.token() == CLASS || S.token() == INTERFACE || allowEnums
					&& S.token() == ENUM) {
				return List.<JCTree> of(classOrInterfaceOrEnumDeclaration(mods,
						dc));
			} else if (S.token() == LBRACE && !isInterface
					&& (mods.flags & Flags.StandardFlags & ~Flags.STATIC) == 0
					&& mods.annotations.isEmpty()) {
				return List.<JCTree> of(block(pos, mods.flags));
			} else {
				pos = S.pos();
				List<JCTypeParameter> typarams = typeParametersOpt();
				// Hack alert: if there are type arguments but no Modifiers, the
				// start
				// position will be lost unless we set the Modifiers position.
				// There
				// should be an AST node for type parameters (BugId 5005090).
				if (typarams.length() > 0 && mods.pos == Position.NOPOS) {
					mods.pos = pos;
				}
				@SuppressWarnings("unused")
				Token token = S.token();
				Name name = S.name();
				pos = S.pos();
				JCExpression type;
				boolean isVoid = S.token() == VOID;
				if (isVoid) {
					type = to(F.at(pos).TypeIdent(TypeTags.VOID));
					S.nextToken();
				} else {
					type = type();
				}
				if (S.token() == LPAREN && !isInterface
						&& type.getTag() == JCTree.IDENT) {
					if (isInterface || name != className)
						log.error(pos, "invalid.meth.decl.ret.type.req");
					return List.of(methodDeclaratorRest(pos, mods, null,
							names.init, typarams, isInterface, true, dc));
				} else {
					pos = S.pos();
					name = ident();
					if (S.token() == LPAREN) {
						return List.of(methodDeclaratorRest(pos, mods, type,
								name, typarams, isInterface, isVoid, dc));
					} else if (!isVoid && typarams.isEmpty()) {
						List<JCTree> defs = variableDeclaratorsRest(pos, mods,
								type, name, isInterface, dc,
								new ListBuffer<JCTree>()).toList();
						storeEnd(defs.last(), S.endPos());
						accept(SEMI);
						return defs;
					} else {
						pos = S.pos();
						List<JCTree> err = isVoid ? List.<JCTree> of(toP(F.at(
								pos).MethodDef(mods, name, type, typarams,
								List.<JCVariableDecl> nil(),
								List.<JCExpression> nil(), null, null))) : null;
						return List.<JCTree> of(syntaxError(S.pos(), err,
								"expected", keywords.token2string(LPAREN)));
					}
				}
			}
		}
	}

	/**
	 * MethodDeclaratorRest = FormalParameters BracketsOpt [Throws TypeList] (
	 * MethodBody | [DEFAULT AnnotationValue] ";") VoidMethodDeclaratorRest =
	 * FormalParameters [Throws TypeList] ( MethodBody | ";")
	 * InterfaceMethodDeclaratorRest = FormalParameters BracketsOpt [THROWS
	 * TypeList] ";" VoidInterfaceMethodDeclaratorRest = FormalParameters
	 * [THROWS TypeList] ";" ConstructorDeclaratorRest = "("
	 * FormalParameterListOpt ")" [THROWS TypeList] MethodBody
	 */
	JCTree methodDeclaratorRest(int pos, JCModifiers mods, JCExpression type,
			Name name, List<JCTypeParameter> typarams, boolean isInterface,
			boolean isVoid, String dc) {
		List<JCVariableDecl> params = formalParameters();
		if (!isVoid)
			type = bracketsOpt(type);
		List<JCExpression> thrown = List.nil();
		if (S.token() == THROWS) {
			S.nextToken();
			thrown = qualidentList();
		}
		JCBlock body = null;
		JCExpression defaultValue;
		if (S.token() == LBRACE) {
			body = block();
			defaultValue = null;
		} else {
			if (S.token() == DEFAULT) {
				accept(DEFAULT);
				defaultValue = annotationValue();
			} else {
				defaultValue = null;
			}
			accept(SEMI);
			if (S.pos() <= errorEndPos) {
				// error recovery
				skip(false, true, false, false);
				if (S.token() == LBRACE) {
					body = block();
				}
			}
		}
		JCMethodDecl result = toP(F.at(pos).MethodDef(mods, name, type,
				typarams, params, thrown, body, defaultValue));
		attach(result, dc);
		return result;
	}

	/**
	 * QualidentList = Qualident {"," Qualident}
	 */
	List<JCExpression> qualidentList() {
		ListBuffer<JCExpression> ts = new ListBuffer<JCExpression>();
		ts.append(qualident());
		while (S.token() == COMMA) {
			S.nextToken();
			ts.append(qualident());
		}
		return ts.toList();
	}

	/**
	 * TypeParametersOpt = ["<" TypeParameter {"," TypeParameter} ">"]
	 */
	List<JCTypeParameter> typeParametersOpt() {
		if (S.token() == LT) {
			checkGenerics();
			ListBuffer<JCTypeParameter> typarams = new ListBuffer<JCTypeParameter>();
			S.nextToken();
			typarams.append(typeParameter());
			while (S.token() == COMMA) {
				S.nextToken();
				typarams.append(typeParameter());
			}
			accept(GT);
			return typarams.toList();
		} else {
			return List.nil();
		}
	}

	/**
	 * TypeParameter = TypeVariable [TypeParameterBound] TypeParameterBound =
	 * EXTENDS Type {"&" Type} TypeVariable = Ident
	 */
	JCTypeParameter typeParameter() {
		int pos = S.pos();
		Name name = ident();
		ListBuffer<JCExpression> bounds = new ListBuffer<JCExpression>();
		if (S.token() == EXTENDS) {
			S.nextToken();
			bounds.append(type());
			while (S.token() == AMP) {
				S.nextToken();
				bounds.append(type());
			}
		}
		return toP(F.at(pos).TypeParameter(name, bounds.toList()));
	}

	/**
	 * FormalParameters = "(" [ FormalParameterList ] ")" FormalParameterList =
	 * [ FormalParameterListNovarargs , ] LastFormalParameter
	 * FormalParameterListNovarargs = [ FormalParameterListNovarargs , ]
	 * FormalParameter
	 */
	List<JCVariableDecl> formalParameters() {
		ListBuffer<JCVariableDecl> params = new ListBuffer<JCVariableDecl>();
		JCVariableDecl lastParam = null;
		accept(LPAREN);
		if (S.token() != RPAREN) {
			params.append(lastParam = formalParameter());
			while ((lastParam.mods.flags & Flags.VARARGS) == 0
					&& S.token() == COMMA) {
				S.nextToken();
				params.append(lastParam = formalParameter());
			}
		}
		accept(RPAREN);
		return params.toList();
	}

	JCModifiers optFinal(long flags) {
		JCModifiers mods = modifiersOpt();
		checkNoMods(mods.flags & ~(Flags.FINAL | Flags.DEPRECATED));
		mods.flags |= flags;
		return mods;
	}

	/**
	 * FormalParameter = { FINAL | '@' Annotation } Type VariableDeclaratorId
	 * LastFormalParameter = { FINAL | '@' Annotation } Type '...' Ident |
	 * FormalParameter
	 */
	JCVariableDecl formalParameter() {
		JCModifiers mods = optFinal(Flags.PARAMETER);
		JCExpression type = type();
		if (S.token() == ELLIPSIS) {
			checkVarargs();
			mods.flags |= Flags.VARARGS;
			type = to(F.at(S.pos()).TypeArray(type));
			S.nextToken();
		}
		return variableDeclaratorId(mods, type);
	}

	/* ---------- auxiliary methods -------------- */

	/**
	 * Check that given tree is a legal expression statement.
	 */
	protected JCExpression checkExprStat(JCExpression t) {
		switch (t.getTag()) {
		case JCTree.PREINC:
		case JCTree.PREDEC:
		case JCTree.POSTINC:
		case JCTree.POSTDEC:
		case JCTree.ASSIGN:
		case JCTree.BITOR_ASG:
		case JCTree.BITXOR_ASG:
		case JCTree.BITAND_ASG:
		case JCTree.SL_ASG:
		case JCTree.SR_ASG:
		case JCTree.USR_ASG:
		case JCTree.PLUS_ASG:
		case JCTree.MINUS_ASG:
		case JCTree.MUL_ASG:
		case JCTree.DIV_ASG:
		case JCTree.MOD_ASG:
		case JCTree.APPLY:
		case JCTree.NEWCLASS:
		case JCTree.ERRONEOUS:
			return t;
		default:
			log.error(t.pos, "not.stmt");
			return F.at(t.pos).Erroneous(List.<JCTree> of(t));
		}
	}

	/**
	 * Return precedence of operator represented by token, -1 if token is not a
	 * binary operator. @see TreeInfo.opPrec
	 */
	static int prec(Token token) {
		int oc = optag(token);
		return (oc >= 0) ? TreeInfo.opPrec(oc) : -1;
	}

	/**
	 * Return operation tag of binary operator represented by token, -1 if token
	 * is not a binary operator.
	 */
	static int optag(Token token) {
		switch (token) {
		case BARBAR:
			return JCTree.OR;
		case AMPAMP:
			return JCTree.AND;
		case BAR:
			return JCTree.BITOR;
		case BAREQ:
			return JCTree.BITOR_ASG;
		case CARET:
			return JCTree.BITXOR;
		case CARETEQ:
			return JCTree.BITXOR_ASG;
		case AMP:
			return JCTree.BITAND;
		case AMPEQ:
			return JCTree.BITAND_ASG;
		case EQEQ:
			return JCTree.EQ;
		case BANGEQ:
			return JCTree.NE;
		case LT:
			return JCTree.LT;
		case GT:
			return JCTree.GT;
		case LTEQ:
			return JCTree.LE;
		case GTEQ:
			return JCTree.GE;
		case LTLT:
			return JCTree.SL;
		case LTLTEQ:
			return JCTree.SL_ASG;
		case GTGT:
			return JCTree.SR;
		case GTGTEQ:
			return JCTree.SR_ASG;
		case GTGTGT:
			return JCTree.USR;
		case GTGTGTEQ:
			return JCTree.USR_ASG;
		case PLUS:
			return JCTree.PLUS;
		case PLUSEQ:
			return JCTree.PLUS_ASG;
		case SUB:
			return JCTree.MINUS;
		case SUBEQ:
			return JCTree.MINUS_ASG;
		case STAR:
			return JCTree.MUL;
		case STAREQ:
			return JCTree.MUL_ASG;
		case SLASH:
			return JCTree.DIV;
		case SLASHEQ:
			return JCTree.DIV_ASG;
		case PERCENT:
			return JCTree.MOD;
		case PERCENTEQ:
			return JCTree.MOD_ASG;
		case INSTANCEOF:
			return JCTree.TYPETEST;
		default:
			return -1;
		}
	}

	/**
	 * Return operation tag of unary operator represented by token, -1 if token
	 * is not a binary operator.
	 */
	static int unoptag(Token token) {
		switch (token) {
		case PLUS:
			return JCTree.POS;
		case SUB:
			return JCTree.NEG;
		case BANG:
			return JCTree.NOT;
		case TILDE:
			return JCTree.COMPL;
		case PLUSPLUS:
			return JCTree.PREINC;
		case SUBSUB:
			return JCTree.PREDEC;
		default:
			return -1;
		}
	}

	/**
	 * Return type tag of basic type represented by token, -1 if token is not a
	 * basic type identifier.
	 */
	static int typetag(Token token) {
		switch (token) {
		case BYTE:
			return TypeTags.BYTE;
		case CHAR:
			return TypeTags.CHAR;
		case SHORT:
			return TypeTags.SHORT;
		case INT:
			return TypeTags.INT;
		case LONG:
			return TypeTags.LONG;
		case FLOAT:
			return TypeTags.FLOAT;
		case DOUBLE:
			return TypeTags.DOUBLE;
		case BOOLEAN:
			return TypeTags.BOOLEAN;
		default:
			return -1;
		}
	}

	void checkGenerics() {
		if (!allowGenerics) {
			log.error(S.pos(), "generics.not.supported.in.source", source.name);
			allowGenerics = true;
		}
	}

	void checkVarargs() {
		if (!allowVarargs) {
			log.error(S.pos(), "varargs.not.supported.in.source", source.name);
			allowVarargs = true;
		}
	}

	void checkForeach() {
		if (!allowForeach) {
			log.error(S.pos(), "foreach.not.supported.in.source", source.name);
			allowForeach = true;
		}
	}

	void checkStaticImports() {
		if (!allowStaticImport) {
			log.error(S.pos(), "static.import.not.supported.in.source",
					source.name);
			allowStaticImport = true;
		}
	}

	void checkAnnotations() {
		if (!allowAnnotations) {
			log.error(S.pos(), "annotations.not.supported.in.source",
					source.name);
			allowAnnotations = true;
		}
	}
}
